package net.oschina.gitapp.ui;

import java.io.File;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import net.oschina.gitapp.AppContext;
import net.oschina.gitapp.AppException;
import net.oschina.gitapp.R;
import net.oschina.gitapp.adapter.ProjectCodeTreeListAdapter;
import net.oschina.gitapp.api.ApiClient;
import net.oschina.gitapp.bean.Branch;
import net.oschina.gitapp.bean.CodeTree;
import net.oschina.gitapp.bean.CommonList;
import net.oschina.gitapp.bean.MoreMenuItem;
import net.oschina.gitapp.bean.Project;
import net.oschina.gitapp.bean.URLs;
import net.oschina.gitapp.common.Contanst;
import net.oschina.gitapp.common.DataRequestThreadHandler;
import net.oschina.gitapp.common.StringUtils;
import net.oschina.gitapp.common.UIHelper;
import net.oschina.gitapp.ui.baseactivity.BaseActionBarActivity;
import net.oschina.gitapp.util.TypefaceUtils;
import net.oschina.gitapp.widget.DropDownMenu;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.os.Vibrator;
import android.support.v4.view.MenuItemCompat;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * 项目代码列表
 * 
 * @created 2014-07-18
 * @author 火蚁（http://my.oschina.net/LittleDY）
 */
public class ProjectCodeActivity extends BaseActionBarActivity implements
        OnItemClickListener, OnItemLongClickListener, OnClickListener {
    
    private final int MORE_MENU_OPENDEFAULT = 00; // 分享
    private final int MORE_MENU_OPEN_WITH_BROWS = 01; // 在浏览器中打开
    
    private final int MENU_REFRESH_ID = 0;
    
    private static final int ACTION_INIT = 0; // 初始化
    private static final int ACTION_REFRESH = 1; // 刷新
    private static final int ACTION_LOADING_TREE = 2; // 加载代码层级树
    private static final int ACTION_PRE_TREE = 3; // 前一级代码树
    
    private Menu optionsMenu;
    
    private Project mProject;
    
    private ListView mCodeTree;
    
    private LinearLayout mSwitch_branch;
    
    private TextView mBranchIcon;
    
    private TextView mBranchName;
    
    private ProjectCodeTreeListAdapter mAdapter;
    
    private Stack<List<CodeTree>> mCodeFolders = new Stack<List<CodeTree>>();
    
    private List<CodeTree> mTrees;
    
    private CodeTree mOnCilkCodeTree;
    
    private String mPath = "";
    
    private String mBranch;
    
    private AppContext mAppContext;
    
    private LinearLayout mContentLayout;
    
    // 上一级目录
    private LinearLayout mCodeTreePreFolder;
    
    private TextView mCodeFloders;
    
    private ProgressBar mLoading;
    
    private ProgressDialog mLoadBranch;
    
    private List<Branch> mBranchList = new ArrayList<Branch>(); // 标签和分支列表
    
    private int mBranchIndex = 0;
    
    private AlertDialog.Builder dialog;
    
    private DataRequestThreadHandler mRequestThreadHandler = new DataRequestThreadHandler();
    
    private DropDownMenu mMoreMenuWindow;
    
    private List<MoreMenuItem> mMoreItems = new ArrayList<MoreMenuItem>();
    
    private View.OnClickListener onMoreMenuItemClick = new OnClickListener() {
        
        @Override
        public void onClick(View v) {
            if (mMoreMenuWindow != null && mMoreMenuWindow.isShowing()) {
                mMoreMenuWindow.dismiss();
            }
            if (mProject == null) {
                return;
            }
            if (!mProject.isPublic()) {
                UIHelper.ToastMessage(mAppContext, "私有项目不支持该操作");
                return;
            }
            int id = v.getId();
            switch (id) {
                case MORE_MENU_OPENDEFAULT:
                    showDetail(mOnCilkCodeTree.getName(), mBranch);
                    break;
                case MORE_MENU_OPEN_WITH_BROWS:
                    openWithBrowser(mOnCilkCodeTree);
                    break;
                default:
                    break;
            }
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.projectcode_fragment);
        mAppContext = getGitApplication();
        Intent intent = getIntent();
        if (intent != null) {
            mProject = (Project) intent.getSerializableExtra(Contanst.PROJECT);
            mTitle = "代码列表";
            mSubTitle = mProject.getOwner().getName() + "/"
                    + mProject.getName();
        }
        mBranch = "master";
        initView();
        setupListView();
        loadDatas("", "master", ACTION_INIT);
        TypefaceUtils.setOcticons(mBranchIcon);
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        mRequestThreadHandler.quit();
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        optionsMenu = menu;
        // 刷新按钮
        MenuItem refreshItem = menu.add(0, MENU_REFRESH_ID, MENU_REFRESH_ID,
                "刷新");
        refreshItem
                .setIcon(R.drawable.abc_ic_menu_moreoverflow_normal_holo_light);
        MenuItemCompat.setShowAsAction(refreshItem,
                MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
        return true;
    }
    
    private void initMoreOption() {
        MoreMenuItem openDefault = new MoreMenuItem(MORE_MENU_OPENDEFAULT,
                R.drawable.more_menu_icon_app, "在该app中打开");
        mMoreItems.add(openDefault);
        
        MoreMenuItem open_with_brows = new MoreMenuItem(
                MORE_MENU_OPEN_WITH_BROWS, R.drawable.more_menu_icon_browser,
                "在浏览器中打开");
        mMoreItems.add(open_with_brows);
        
        mMoreMenuWindow.addItems(mMoreItems);
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        
        int id = item.getItemId();
        switch (id) {
            case MENU_REFRESH_ID:
                loadDatas(mPath, mBranch, ACTION_REFRESH);
                break;
        
        }
        return super.onOptionsItemSelected(item);
    }
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (!StringUtils.isEmpty(mPath)) {
                loadPreFolder();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }
    
    private void initView() {
        
        mContentLayout = (LinearLayout) findViewById(R.id.projectcode_content_layout);
        
        mCodeTreePreFolder = (LinearLayout) findViewById(R.id.projectcode_tree_prefolder);
        
        mCodeFloders = (TextView) findViewById(R.id.projectcode_floders);
        
        mCodeFloders.setMovementMethod(LinkMovementMethod.getInstance());
        
        TypefaceUtils
                .setOcticons((TextView) findViewById(R.id.projectcode_pre_img));
        
        // mCodeTreePreFolder.setOnClickListener(this);
        
        mLoading = (ProgressBar) findViewById(R.id.projectcode_loading);
        
        mCodeTree = (ListView) findViewById(R.id.projectcode_tree);
        mSwitch_branch = (LinearLayout) findViewById(R.id.projectcode_switch_branch);
        mBranchIcon = (TextView) findViewById(R.id.projectcode_branch_icon);
        mBranchName = (TextView) findViewById(R.id.projectcode_branch_name);
    }
    
    private void setupListView() {
        mTrees = new ArrayList<CodeTree>();
        mAdapter = new ProjectCodeTreeListAdapter(getGitApplication(), mTrees,
                R.layout.projectcodetree_listitem);
        mCodeTree.setAdapter(mAdapter);
        mCodeTree.setOnItemClickListener(this);
        mCodeTree.setOnItemLongClickListener(this);
        mSwitch_branch.setOnClickListener(this);
    }
    
    private void loadDatas(final String path, final String ref_name, int action) {
        mRequestThreadHandler.request(0, new AsyncDataHandler(path, ref_name,
                action));
    }
    
    /**
     * 加载上一级代码树
     */
    private void loadPreFolder() {
        mTrees.clear();
        mCodeFolders.pop();
        mTrees.addAll(mCodeFolders.peek());
        mAdapter.notifyDataSetChanged();
        savePathAndBranch(getPrePath(), mBranch);
    }
    
    private void beforeLoading(int action) {
        if (action == ACTION_REFRESH) {
            mContentLayout.setVisibility(View.GONE);
            mSwitch_branch.setVisibility(View.GONE);
            mLoading.setVisibility(View.VISIBLE);
        }
        else if (action != ACTION_INIT) {
            MenuItemCompat.setActionView(optionsMenu.findItem(MENU_REFRESH_ID),
                    R.layout.actionbar_indeterminate_progress);
        }
    }
    
    private void afterLoading(int action) {
        if (action == ACTION_INIT || action == ACTION_REFRESH) {
            mContentLayout.setVisibility(View.VISIBLE);
            mSwitch_branch.setVisibility(View.VISIBLE);
            mLoading.setVisibility(View.GONE);
        }
        else {
            MenuItemCompat.setActionView(optionsMenu.findItem(MENU_REFRESH_ID),
                    null);
        }
    }
    
    /**
     * 记录路径和分支
     * 
     * @param _mPath
     * @param _mRef_name
     */
    private void savePathAndBranch(String path, String branch) {
        mPath = path;
        mBranch = branch;
        if (StringUtils.isEmpty(mPath)) {
            mCodeTreePreFolder.setVisibility(View.GONE);
        }
        else {
            mCodeTreePreFolder.setVisibility(View.VISIBLE);
        }
        String floders = mProject.getName()
                + (StringUtils.isEmpty(mPath) ? "" : "/" + mPath);
        PathString ps = new PathString(floders);
        mCodeFloders.setText(ps);
    }
    
    // 加载代码树
    private class AsyncDataHandler implements
            DataRequestThreadHandler.AsyncDataHandler<Message> {
        
        private String _mPath;
        private String _mRef_name;
        private int _mAction;
        
        AsyncDataHandler(String path, String ref_name, int action) {
            _mPath = path;
            _mRef_name = ref_name;
            _mAction = action;
        }
        
        // 加载前
        @Override
        public void onPreExecute() {
            beforeLoading(_mAction);
        }
        
        // 加载ing
        @Override
        public Message execute() {
            Message msg = new Message();
            try {
                boolean refresh = true;
                if (_mAction == ACTION_INIT) {
                    refresh = false;
                }
                
                CommonList<CodeTree> list = mAppContext.getProjectCodeTree(
                        StringUtils.toInt(mProject.getId()), _mPath,
                        _mRef_name, refresh);
                List<CodeTree> tree = list.getList();
                
                msg.what = 1;
                msg.obj = tree;
            }
            catch (Exception e) {
                msg.what = -1;
                msg.obj = e;
            }
            return msg;
        }
        
        // 加载完成
        @SuppressWarnings("unchecked")
        @Override
        public void onPostExecute(Message result) {
            afterLoading(_mAction);
            if (result.what == 1 && result.obj != null) {
                mTrees.clear();
                mTrees.addAll((List<CodeTree>) result.obj);
                // 加载成功，记录相关信息
                if (_mAction == ACTION_LOADING_TREE
                        || _mAction == ACTION_PRE_TREE) {
                    savePathAndBranch(_mPath, _mRef_name);
                }
                mAdapter.notifyDataSetChanged();
                mCodeFolders.push((List<CodeTree>) result.obj);
            }
            else {
                if (result.obj instanceof AppException) {
                    ((AppException) result.obj).makeToast(mAppContext);
                }
                else {
                    UIHelper.ToastMessage(getActivity(),
                            ((Exception) result.obj).getMessage());
                }
            }
        }
    }
    
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        
        position -= mCodeTree.getHeaderViewsCount();
        
        mOnCilkCodeTree = mTrees.get(position);
        if (mOnCilkCodeTree.getType().equalsIgnoreCase(CodeTree.TYPE_TREE)) {
            loadDatas(getPath(mOnCilkCodeTree.getName()), mBranch,
                    ACTION_LOADING_TREE);
        }
        else {
            checkShow(mOnCilkCodeTree);
        }
    }
    
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view,
            int position, long id) {
        position -= mCodeTree.getHeaderViewsCount();
        // 震动
        Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
        vibrator.vibrate(80);
        mOnCilkCodeTree = mTrees.get(position);
        if (mOnCilkCodeTree.getType().equalsIgnoreCase(CodeTree.TYPE_BLOB)) {
            if (mMoreMenuWindow == null) {
                mMoreMenuWindow = new DropDownMenu(ProjectCodeActivity.this,
                        onMoreMenuItemClick);
                initMoreOption();
                mMoreMenuWindow.getViewGroup().setPadding(0, 0, 0, 0);
            }
            mMoreMenuWindow.showAtLocation(view, Gravity.CENTER, 0, 0);
        }
        return true;
    }
    
    // 获得访问的路径
    private String getPath(String path) {
        if (mPath == null || StringUtils.isEmpty(mPath)) {
            return path;
        }
        else {
            return mPath + "/" + path;
        }
    }
    
    // 获得上一级路径
    private String getPrePath() {
        int index = mPath.lastIndexOf("/");
        
        return index != -1 ? mPath.substring(0, index) : "";
    }
    
    /**
     * 判断code的文件的类型显示不同的操作
     * 
     * @param codeTree
     */
    @SuppressWarnings("deprecation")
    private void checkShow(CodeTree codeTree) {
        
        String fileName = codeTree.getName();
        
        if (isCodeTextFile(fileName)) {
            
            showDetail(fileName, mBranch);
            
        }
        else if (isImage(fileName)) {
            String url = URLs.URL_HOST + mProject.getOwner().getUsername()
                    + URLs.URL_SPLITTER + mProject.getPath()
                    + URLs.URL_SPLITTER + "raw" + URLs.URL_SPLITTER + mBranch
                    + URLs.URL_SPLITTER
                    + URLEncoder.encode(getFilePath(fileName))
                    + "?private_token=" + ApiClient.getToken(mAppContext);
            UIHelper.showImageZoomActivity(ProjectCodeActivity.this, url);
        }
        else {
            openWithBrowser(codeTree);
        }
    }
    
    /**
     * 在浏览器中在打开
     * 
     * @param codeTree
     */
    @SuppressWarnings("deprecation")
    private void openWithBrowser(CodeTree codeTree) {
        String url = URLs.URL_HOST + mProject.getOwner().getUsername()
                + URLs.URL_SPLITTER + mProject.getPath() + URLs.URL_SPLITTER
                + "blob" + URLs.URL_SPLITTER + mBranch + URLs.URL_SPLITTER
                + URLEncoder.encode(getFilePath(codeTree.getName()))
                + "?private_token=" + ApiClient.getToken(mAppContext);
        UIHelper.openBrowser(ProjectCodeActivity.this, url);
    }
    
    // 判断是不是代码文件
    private boolean isCodeTextFile(String fileName) {
        boolean res = false;
        // 文件的后缀
        int index = fileName.lastIndexOf(".");
        if (index > 0) {
            fileName = fileName.substring(index);
        }
        String codeFileSuffix[] = new String[] { ".java", ".confg", ".ini",
                ".xml", ".json", ".txt", ".go", ".php", ".php3", ".php4",
                ".php5", ".js", ".css", ".html", ".properties", ".c", ".hpp",
                ".h", ".hh", ".cpp", ".cfg", ".rb", ".example", ".gitignore",
                ".project", ".classpath", ".m", ".md", ".rst", ".vm", ".cl",
                ".py", ".pl", ".haml", ".erb", ".scss", ".bat", ".coffee",
                ".as", ".sh", ".m", ".pas", ".cs", ".groovy", ".scala", ".sql",
                ".bas", ".xml", ".vb", ".xsl", ".swift", ".ftl", ".yml", ".ru",
                ".jsp", ".markdown", ".cshap", ".apsx", ".sass", ".less",
                ".ftl", ".haml", ".log", ".tx", ".csproj", ".sln", ".clj",
                ".scm", ".xhml", ".xaml", ".lua" };
        for (String string : codeFileSuffix) {
            if (fileName.equalsIgnoreCase(string)) {
                res = true;
            }
        }
        
        // 特殊的文件
        String fileNames[] = new String[] { "LICENSE", "TODO", "README",
                "readme", "makefile", "gemfile", "gemfile.*", "gemfile.lock",
                "CHANGELOG" };
        
        for (String string : fileNames) {
            if (fileName.equalsIgnoreCase(string)) {
                res = true;
            }
        }
        
        return res;
    }
    
    // 判断是否是图片
    private boolean isImage(String fileName) {
        boolean res = false;
        // 图片后缀
        int index = fileName.lastIndexOf(".");
        if (index > 0) {
            fileName = fileName.substring(index);
        }
        String imageSuffix[] = new String[] { ".png", ".jpg", ".jpeg", ".jpe",
                ".bmp", ".exif", ".dxf", ".wbmp", ".ico", ".jpe", ".gif",
                ".pcx", ".fpx", ".ufo", ".tiff", ".svg", ".eps", ".ai", ".tga",
                ".pcd", ".hdri" };
        for (String string : imageSuffix) {
            if (fileName.equalsIgnoreCase(string)) {
                res = true;
            }
        }
        return res;
    }
    
    // 查看代码文件详情
    private void showDetail(String fileName, String ref) {
        Intent intent = new Intent(getGitApplication(),
                CodeFileDetailActivity.class);
        Bundle bundle = new Bundle();
        bundle.putSerializable(Contanst.PROJECT, mProject);
        bundle.putString("fileName", fileName);
        bundle.putString("path", getFilePath(fileName));
        bundle.putString("ref", ref);
        intent.putExtras(bundle);
        startActivity(intent);
    }
    
    /**
     * 获得文件的访问路径
     * 
     * @param fileName
     * @return
     */
    private String getFilePath(String fileName) {
        return mPath == null || StringUtils.isEmpty(mPath) ? fileName : mPath
                + "/" + fileName;
    }
    
    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.projectcode_switch_branch:
                switchBranch();
                break;
            // case R.id.projectcode_tree_prefolder:
            // if (StringUtils.isEmpty(mPath))
            // {
            // return;
            // }
            // loadPreFolder();
            // break;
            default:
                break;
        }
    }
    
    private void switchBranch() {
        if (mProject == null) {
            return;
        }
        if (dialog == null || mBranchList.isEmpty()) {
            loadBranchAndTags(true);
        }
        else {
            loadBranchAndTags(false);
        }
    }
    
    // 加载分支和标签数据
    private void loadBranchAndTags(final boolean isRefalsh) {
        
        // 异步
        new AsyncTask<Void, Void, Message>() {
            @Override
            protected Message doInBackground(Void... params) {
                Message msg = new Message();
                try {
                    msg.what = 1;
                    if (!isRefalsh) {
                        return msg;
                    }
                    // 1.加载分支
                    CommonList<Branch> branchs = mAppContext
                            .getProjectBranchsOrTagsLsit(mProject.getId(), 1,
                                    Branch.TYPE_BRANCH, isRefalsh);
                    for (Branch branch : branchs.getList()) {
                        // 设置为分支类型
                        branch.setType(Branch.TYPE_BRANCH);
                        mBranchList.add(branch);
                    }
                    
                    // 2.加载标签
                    CommonList<Branch> tags = mAppContext
                            .getProjectBranchsOrTagsLsit(mProject.getId(), 1,
                                    Branch.TYPE_TAG, isRefalsh);
                    for (Branch branch : tags.getList()) {
                        // 设置为标签类型
                        branch.setType(Branch.TYPE_TAG);
                        mBranchList.add(branch);
                    }
                }
                catch (Exception e) {
                    msg.what = -1;
                    msg.obj = e;
                }
                return msg;
            }
            
            @Override
            protected void onPreExecute() {
                if (mLoadBranch == null) {
                    mLoadBranch = new ProgressDialog(getActivity());
                    mLoadBranch.setCancelable(true);
                    mLoadBranch.setCanceledOnTouchOutside(false);
                    mLoadBranch.setMessage("加载分支和标签...");
                    mLoadBranch.setProgressStyle(R.style.Spinner);
                    mLoadBranch.show();
                }
                if (dialog == null) {
                    dialog = new AlertDialog.Builder(getActivity())
                            .setTitle("选择分支或标签");
                }
            }
            
            @Override
            protected void onPostExecute(Message msg) {
                if (mLoadBranch != null) {
                    mLoadBranch.dismiss();
                }
                if (msg.what == 1) {
                    final String baArrays[] = new String[mBranchList.size()];
                    for (int i = 0; i < mBranchList.size(); i++) {
                        baArrays[i] = mBranchList.get(i).getName();
                        if (baArrays[i].equals(mBranch)) {
                            mBranchIndex = i;
                        }
                    }
                    dialog.setSingleChoiceItems(baArrays, mBranchIndex,
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog,
                                        int which) {
                                    dialog.dismiss();
                                    if (which == mBranchIndex) {
                                        return;
                                    }
                                    mBranchIndex = which;
                                    mBranch = baArrays[which];
                                    mPath = "";
                                    loadDatas(mPath, mBranch, ACTION_REFRESH);
                                    mBranchName.setText(mBranch);
                                }
                            }).setNegativeButton("取消", null);
                    dialog.show();
                }
                else {
                    if (msg.obj instanceof AppException) {
                        ((AppException) msg.obj).makeToast(getActivity());
                    }
                    else {
                        UIHelper.ToastMessage(mAppContext, "加载分支和标签失败");
                    }
                }
            }
        }.execute();
    }
    
    public void onPathClick(String path, int index) {
        // loadDatas(path, mBranch, ACTION_LOADING_TREE);
        // 使用缓存数据
        mTrees.clear();
        
        // 移除当前层级之后的数据
        for (int i = 0, count = mCodeFolders.size() - index; i < count - 1; i++) {
            mCodeFolders.pop();
        }
        
        mTrees.addAll(mCodeFolders.peek());
        mAdapter.notifyDataSetChanged();
        savePathAndBranch(path, mBranch);
    }
    
    class PathString extends SpannableString {
        public PathString(String text) {
            super(text);
            setup(text);
        }
        
        private void setup(String text) {
            int start = 0;
            int chatIndex = text.indexOf(File.separatorChar);
            int pathStart = chatIndex + 1;// 路径String位置，text最开始为工程名称，不包含在Path内，所以标注开始位置用于截取Path
            int pathIndex = 0;// 标注层级，用于获取缓存
            while (chatIndex >= 0) {
                String path = chatIndex > pathStart ? text.substring(pathStart,
                        chatIndex) : "";
                setSpan(new Clickable(path, pathIndex), start, chatIndex,
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                pathIndex++;
                start = chatIndex + 1;
                chatIndex = text.indexOf(File.separatorChar, start);
            }
        }
        
        class Clickable extends ClickableSpan {
            private final String mPath;
            private final int mIndex;
            
            public Clickable(String path, int index) {
                mPath = path;
                mIndex = index;
            }
            
            @Override
            public void onClick(View widget) {
                onPathClick(mPath, mIndex);
            }
        }
    }
}
